package com.example.mybatislearn.common.mapper;


import com.example.mybatislearn.common.lang.Pair;
import com.example.mybatislearn.common.mapper.converter.SmartCustomConverter;
import com.example.mybatislearn.common.mapper.converter.SmartFieldCustomConverter;
import com.example.mybatislearn.common.mapper.converter.customer.SmartTypeConverter;
import com.example.mybatislearn.common.mapper.exception.SmartMapperException;
import com.example.mybatislearn.common.mapper.mapping.SmartBeanMapping;
import com.example.mybatislearn.common.mapper.mapping.SmartFieldMapping;
import com.example.mybatislearn.common.mapper.util.ResolveTypeUtils;
import org.dozer.DozerBeanMapper;
import org.dozer.MappingException;
import org.dozer.loader.DozerBuilder;
import org.dozer.loader.api.TypeMappingBuilder;

import java.util.ArrayList;
import java.util.List;

import static org.dozer.loader.api.FieldsMappingOptions.customConverter;

public class SmartDozerMapper implements SmartMapper {

    private DozerBeanMapper dozerBeanMapper = new DozerBeanMapper();

    private DozerBuilder.ConfigurationBuilder config;

    public void addBeanMapping(SmartBeanMapping beanMapping) {

        try {
            dozerBeanMapper.addMapping(new SmartBeanMappingBuilder() {

                @Override
                protected void configure() {
                    TypeMappingBuilder typeMappingBuilder = mapping(beanMapping.getSrcType(), beanMapping.getDestType());
                    List<SmartFieldMapping> fieldMappingList = beanMapping.getFieldMappings();
                    for (SmartFieldMapping fieldMapping : fieldMappingList) {
                        if (fieldMapping.getTypeConverter() == null) {
                            typeMappingBuilder.fields(fieldMapping.getSrcFieldName(), fieldMapping.getDestFieldName());
                        } else {
                            SmartFieldCustomConverter.addFieldTypeConverter(beanMapping.getSrcType(), beanMapping.getDestType(), fieldMapping.getSrcFieldName(), fieldMapping.getDestFieldName(), fieldMapping.getTypeConverter());
                            String parameter = beanMapping.getSrcType().getName() + "," + beanMapping.getDestType().getName() + "|" + fieldMapping.getSrcFieldName() + "," + fieldMapping.getDestFieldName();
                            typeMappingBuilder.fields(fieldMapping.getSrcFieldName(), fieldMapping.getDestFieldName(), customConverter(SmartFieldCustomConverter.class, parameter));
                        }
                    }
                }
            });
        } catch (Exception e) {
            throw new SmartMapperException(e.getMessage(), e);
        }

    }

    public void addCustomConterver(SmartTypeConverter converter) {
        Pair<Class, Class> typeConverterKey = ResolveTypeUtils.resolveTypeConverterKey(converter.getClass());
        SmartCustomConverter.addSmartTypeConverter(converter);
        if(config == null) {
            dozerBeanMapper.addMapping(new SmartBeanMappingBuilder() {
                @Override
                protected void configure() {
                    config = this.getDozerBuilder().configuration();
                }
            });
            config.customConverter(SmartCustomConverter.class).classA(typeConverterKey.getKey()).classB(typeConverterKey.getValue());
        }
    }


    @Override
    public <T> T map(Object source, Class<T> destinationClass) throws SmartMapperException {
        try {
            return dozerBeanMapper.map(source, destinationClass);
        }catch (MappingException e) {
            throw  new SmartMapperException(e.getMessage(),e);
        }
    }

    @Override
    public void map(Object source, Object destination) throws SmartMapperException {
        try {
            dozerBeanMapper.map(source,destination);
        }catch (MappingException e) {
            throw  new SmartMapperException(e.getMessage(),e);
        }
    }

    @Override
    public <T> List<T> mapList(List<Object> source, Class<T> destinationClass) throws SmartMapperException {
        if(source == null) {
            return null;
        }
        try {
            List<T> ret = new ArrayList<>();
            for (Object element : source) {
                T des = map(element, destinationClass);
                ret.add(des);
            }
            return ret;
        }catch (MappingException e) {
            throw  new SmartMapperException(e.getMessage(),e);
        }
    }

    @Override
    public void map(List<Object> source, List<Object> destination) throws SmartMapperException {
        if(source == null) {
            return;
        }
        try {
            for (int i = 0; i < source.size(); i++) {
                map(source.get(i), destination.get(i));
            }
        }catch (MappingException e) {
            throw  new SmartMapperException(e.getMessage(),e);
        }
    }
}
